package com.question.common.aspect;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.question.common.annotation.DataIgnoreConvert;
import com.question.common.enums.DataIgnoreConvertEnum;
import com.question.common.model.dto.DataConvertDTO;
import com.question.common.model.dto.DataTypeConvertDTO;
import com.question.common.model.dto.DynamicFieldDTO;
import com.question.common.service.IDataConvert;
import com.question.common.utils.DynamicClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 数据转换
 *
 * @author star
 * @since 2023/9/6
 */
@Slf4j
@Aspect
@Component
public class DataConvertAspect {

    @Resource
    private List<IDataConvert> dataConverts;

    @Pointcut("@annotation(com.question.common.annotation.DataConvert)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object result = point.proceed();
        MethodSignature sign = (MethodSignature) point.getSignature();
        Method method = sign.getMethod();
        DataIgnoreConvert ignoreConvert = method.getAnnotation(DataIgnoreConvert.class);
        if (Objects.isNull(ignoreConvert) || !DataIgnoreConvertEnum.ALL.equals(ignoreConvert.type())) {
            try {
                if (Objects.nonNull(result)) {
                    if (result instanceof Page) {
                        Page page = (Page) result;
                        List records = page.getRecords();
                        page.setRecords(convertListData(ignoreConvert, records));
                        return page;
                    } else if (result instanceof List) {
                        List records = (List) result;
                        return convertListData(ignoreConvert, records);
                    } else {
                        Map<String, DataConvertDTO> fieldMap = getConvertFieldMap(result);
                        return convertData(ignoreConvert, result, fieldMap);
                    }
                }
            } catch (Exception e) {
                log.warn("数据转换失败：", e);
            }
        }
        return result;
    }

    /**
     * 转换用户集合数据
     *
     * @param ignoreConvert 忽略
     * @param records       记录集
     * @return List
     */
    private List convertListData(DataIgnoreConvert ignoreConvert, List records) throws Exception {
        if (CollectionUtils.isNotEmpty(records)) {
            Object object = records.stream().filter(Objects::nonNull).findFirst().orElse(null);
            if (Objects.nonNull(object)) {
                Map<String, DataConvertDTO> fieldMap = getConvertFieldMap(object);
                if (CollectionUtils.isNotEmpty(fieldMap)) {
                    List target = new ArrayList<>(records.size());
                    for (Object data : records) {
                        Object temp = convertData(ignoreConvert, data, fieldMap);
                        target.add(temp);
                    }
                    return target;
                }
            }
        }
        return records;
    }

    /**
     * 转换用户数据
     *
     * @param ignoreConvert 忽略
     * @param source        源数据
     * @param fieldMap      转换字段
     * @return Object
     */
    private Object convertData(DataIgnoreConvert ignoreConvert, Object source, Map<String, DataConvertDTO> fieldMap) throws Exception {
        if (Objects.nonNull(source) && CollectionUtils.isNotEmpty(fieldMap)) {
            List<DynamicFieldDTO> dynamicFieldList = handleDynamicField(ignoreConvert, source, fieldMap);
            if (CollectionUtils.isNotEmpty(dynamicFieldList)) {
                return DynamicClassUtil.dynamicField(source, dynamicFieldList);
            }
        }
        return source;
    }

    private List<DynamicFieldDTO> handleDynamicField(DataIgnoreConvert ignoreConvert, Object data, Map<String, DataConvertDTO> fieldMap) {
        List<DynamicFieldDTO> dynamicFieldList = new ArrayList<>();
        for (Map.Entry<String, DataConvertDTO> entry : fieldMap.entrySet()) {
            String sourceFieldName = entry.getKey();
            DataConvertDTO convertDto = entry.getValue();
            Field sourceField = ReflectionUtils.findField(data.getClass(), sourceFieldName);
            List<DataTypeConvertDTO> typeConverts = convertDto.getTypes();
            for (DataTypeConvertDTO typeConvert : typeConverts) {
                String targetFieldName = typeConvert.getTargetField();
                if (Objects.nonNull(sourceField)) {
                    // 设置字段可访问
                    ReflectionUtils.makeAccessible(sourceField);
                    Object sourceFieldValue = ReflectionUtils.getField(sourceField, data);
                    if (Objects.nonNull(sourceFieldValue)) {
                        Field targetField = ReflectionUtils.findField(data.getClass(), targetFieldName);
                        String convert = null;
                        IDataConvert dataConvert = typeConvert.getDataConvert();
                        boolean ignore = dataConvert.isIgnore(Objects.nonNull(ignoreConvert) ? ignoreConvert.type() : null, sourceField);
                        if (!ignore) {
                            convert = dataConvert.convert(sourceFieldName, typeConvert.getAnnotation(), sourceFieldValue);
                        }
                        if (Objects.nonNull(targetField)) {
                            if (StringUtils.isNotBlank(convert)) {
                                // 设置字段可访问
                                ReflectionUtils.makeAccessible(targetField);
                                ReflectionUtils.setField(targetField, data, convert);
                                continue;
                            }
                        } else {
                            dynamicFieldList.add(DynamicFieldDTO.builder()
                                    .targetFieldName(targetFieldName)
                                    .targetFieldValue(convert)
                                    .build());
                            continue;
                        }
                    }
                }
                dynamicFieldList.add(DynamicFieldDTO.builder()
                        .targetFieldName(targetFieldName)
                        .targetFieldValue(null)
                        .build());
            }
        }
        return dynamicFieldList;
    }

    @NotNull
    private Map<String, DataConvertDTO> getConvertFieldMap(@NotNull Object object) {
        Map<String, DataConvertDTO> fieldMap = new HashMap<>(dataConverts.size());
        ReflectionUtils.doWithFields(object.getClass(), field -> {
            String sourceFieldName = field.getName();
            List<DataTypeConvertDTO> types = new ArrayList<>();
            for (IDataConvert dataConvert : dataConverts) {
                DataTypeConvertDTO typeConvert = dataConvert.confirmType(field);
                if (Objects.nonNull(typeConvert)) {
                    types.add(typeConvert);
                }
            }
            if (CollectionUtils.isNotEmpty(types)) {
                fieldMap.put(sourceFieldName, DataConvertDTO.builder()
                        .sourceField(field)
                        .sourceFieldName(sourceFieldName)
                        .types(types)
                        .build());
            }
        });
        return fieldMap;
    }

}
